// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

package org.citra.citra_emu.fragments

import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Choreographer
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.PixelCopy
import android.view.Surface
import android.view.SurfaceHolder
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.PreferenceManager
import com.aiwu.InputManager
import com.aiwu.MyApp
import com.aiwu.NetPlayManager
import com.aiwu.StateManager
import com.aiwu.dialog.FastSettingDialog
import com.aiwu.dialog.SwitchScreenLayoutDialog
import com.aiwu.library.AiwuSimulator
import com.aiwu.library.App
import com.aiwu.library.OperateManager
import com.aiwu.library.abs.AbsStateManager
import com.aiwu.library.bean.ClickOperateButtonBean
import com.aiwu.library.bean.Menu
import com.aiwu.library.bean.OneKeyOperate
import com.aiwu.library.bean.OneKeyOperateButtonBean
import com.aiwu.library.interfaces.OperateListener
import com.aiwu.library.interfaces.SimulatorCallback
import com.aiwu.library.util.ColorUtil
import com.aiwu.library.util.DialogUtil
import com.aiwu.library.util.IOUtil
import com.aiwu.library.util.ToastUtil
import com.aiwu.memory.MemoryDialogActivity
import com.aiwu.translate.util.ImageUtils
import com.aiwu.utils.CitraUtil
import com.aiwu.utils.GameIdUtil
import com.aiwu.utils.PrefsHelper
import com.aiwu.utils.getBooleanValue
import com.aiwu.utils.saveValue
import com.aiwu.utils.saveValueAsBoolean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.R
import org.citra.citra_emu.activities.EmulationActivity
import org.citra.citra_emu.databinding.FragmentEmulationBinding
import org.citra.citra_emu.display.ScreenAdjustmentUtil
import org.citra.citra_emu.features.settings.model.FloatSetting
import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.ui.main.MainActivity
import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.citra.citra_emu.utils.EmulationLifecycleUtil
import org.citra.citra_emu.utils.GameIconUtils
import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.ViewUtils
import org.citra.citra_emu.viewmodel.EmulationViewModel
import java.io.File
import kotlin.math.ceil

class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback,
    AbsStateManager.OnStateCallback {
    private val preferences: SharedPreferences
        get() = PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)

    private lateinit var emulationState: EmulationState

    private lateinit var emulationActivity: EmulationActivity

    private var _binding: FragmentEmulationBinding? = null
    private val binding get() = _binding!!

    private lateinit var game: Game
    private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil

    private val emulationViewModel: EmulationViewModel by activityViewModels()
    private val settingsViewModel: SettingsViewModel by viewModels()

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is EmulationActivity) {
            emulationActivity = context
            NativeLibrary.setEmulationActivity(context)
        } else {
            throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
        }
    }

    /**
     * Initialize anything that doesn't depend on the layout / views in here.
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        try {
            game = requireActivity().intent.getParcelableExtra("game")!!
        } catch (e: NullPointerException) {
            Toast.makeText(
                requireContext(),
                R.string.no_game_present,
                Toast.LENGTH_SHORT
            ).show()
            requireActivity().finish()
            return
        }

        // So this fragment doesn't restart on configuration changes; i.e. rotation.
        retainInstance = true
        emulationState = EmulationState(game.path)
        emulationActivity = requireActivity() as EmulationActivity
        screenAdjustmentUtil =
            ScreenAdjustmentUtil(emulationActivity.windowManager, settingsViewModel.settings)
        EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() })
        EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() })
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentEmulationBinding.inflate(inflater)
        return binding.root
    }

    // This is using the correct scope, lint is just acting up
    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (requireActivity().isFinishing) {
            return
        }

        binding.surfaceEmulation.holder.addCallback(this)

        GameIconUtils.loadGameIcon(requireActivity(), game, binding.loadingImage)
        binding.loadingTitle.text = game.title

        viewLifecycleOwner.lifecycleScope.apply {
            launch {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    emulationViewModel.shaderProgress.collectLatest {
                        if (it > 0 && it != emulationViewModel.totalShaders.value) {
                            binding.loadingProgressIndicator.isIndeterminate = false
                            binding.loadingProgressText.visibility = View.VISIBLE
                            binding.loadingProgressText.text = String.format(
                                "%d/%d",
                                emulationViewModel.shaderProgress.value,
                                emulationViewModel.totalShaders.value
                            )

                            if (it < binding.loadingProgressIndicator.max) {
                                binding.loadingProgressIndicator.progress = it
                            }
                        }

                        if (it == emulationViewModel.totalShaders.value) {
                            binding.loadingText.setText(R.string.loading)
                            binding.loadingProgressIndicator.isIndeterminate = true
                            binding.loadingProgressText.visibility = View.GONE
                        }
                    }
                }
            }
            launch {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    emulationViewModel.totalShaders.collectLatest {
                        binding.loadingProgressIndicator.max = it
                    }
                }
            }
            launch {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    emulationViewModel.shaderMessage.collectLatest {
                        if (it != "") {
                            binding.loadingText.text = it
                        }
                    }
                }
            }
            launch {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    emulationViewModel.emulationStarted.collectLatest { started ->
                        if (started) {
                            //游戏加载完成
                            ViewUtils.hideView(binding.loadingIndicator)
                        }
                    }
                }
            }
        }
        init25()
    }

    private fun togglePause() {
        if (emulationState.isPaused) {
            emulationState.unpause()
        } else {
            emulationState.pause()
        }
    }

    override fun onResume() {
        super.onResume()
        Choreographer.getInstance().postFrameCallback(this)
        if (NativeLibrary.isRunning()) {
            if (!AiwuSimulator.isEmulatorPausing()) {
                NativeLibrary.unPauseEmulation()
            }
            return
        }

        if (DirectoryInitialization.areCitraDirectoriesReady()) {
            emulationState.run(emulationActivity.isActivityRecreated)
        } else {
            setupCitraDirectoriesThenStartEmulation()
        }
    }

    override fun onPause() {
        if (NativeLibrary.isRunning()) {
            emulationState.pause()
        }
        Choreographer.getInstance().removeFrameCallback(this)
        super.onPause()
    }

    override fun onDetach() {
        NativeLibrary.clearEmulationActivity()
        super.onDetach()
    }

    private fun setupCitraDirectoriesThenStartEmulation() {
        val directoryInitializationState = DirectoryInitialization.start()
        if (directoryInitializationState ===
            DirectoryInitializationState.CITRA_DIRECTORIES_INITIALIZED
        ) {
            emulationState.run(emulationActivity.isActivityRecreated)
        } else if (directoryInitializationState ===
            DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED
        ) {
            Toast.makeText(context, R.string.write_permission_needed, Toast.LENGTH_SHORT)
                .show()
        } else if (directoryInitializationState ===
            DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE
        ) {
            Toast.makeText(
                context,
                R.string.external_storage_not_mounted,
                Toast.LENGTH_SHORT
            ).show()
        }
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        // We purposely don't do anything here.
        // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height)
        emulationState.newSurface(holder.surface, width, height)
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        emulationState.clearSurface()
    }

    override fun doFrame(frameTimeNanos: Long) {
        Choreographer.getInstance().postFrameCallback(this)
        NativeLibrary.doFrame()
    }

    private class EmulationState(private val gamePath: String) {
        private var state: State
        var surface: Surface? = null
            private set
        var surfaceWidth: Int? = null
            private set
        var surfaceHeight: Int? = null
            private set

        init {
            // Starting state is stopped.
            state = State.STOPPED
        }

        @get:Synchronized
        val isStopped: Boolean
            get() = state == State.STOPPED

        @get:Synchronized
        val isPaused: Boolean
            // Getters for the current state
            get() = state == State.PAUSED

        @get:Synchronized
        val isRunning: Boolean
            get() = state == State.RUNNING

        @Synchronized
        fun stop() {
            if (state != State.STOPPED) {
                Log.debug("[EmulationFragment] Stopping emulation.")
                state = State.STOPPED
                NativeLibrary.stopEmulation()
            } else {
                Log.warning("[EmulationFragment] Stop called while already stopped.")
            }
        }

        // State changing methods
        @Synchronized
        fun pause() {
            if (state != State.PAUSED) {
                state = State.PAUSED
                Log.debug("[EmulationFragment] Pausing emulation.")

                // Release the surface before pausing, since emulation has to be running for that.
                //fix闪退
//                NativeLibrary.surfaceDestroyed()
                NativeLibrary.pauseEmulation()
            } else {
                Log.warning("[EmulationFragment] Pause called while already paused.")
            }
        }

        @Synchronized
        fun unpause() {
            if (state != State.RUNNING && !AiwuSimulator.isEmulatorPausing()) {
                state = State.RUNNING
                Log.debug("[EmulationFragment] Unpausing emulation.")

                NativeLibrary.unPauseEmulation()
            } else {
                Log.warning("[EmulationFragment] Unpause called while already running.")
            }
        }

        @Synchronized
        fun run(isActivityRecreated: Boolean) {
            if (isActivityRecreated) {
                if (NativeLibrary.isRunning()) {
                    state = State.PAUSED
                }
            } else {
                Log.debug("[EmulationFragment] activity resumed or fresh start")
            }

            // If the surface is set, run now. Otherwise, wait for it to get set.
            if (surface != null) {
                runWithValidSurface()
            }
        }

        // Surface callbacks
        @Synchronized
        fun newSurface(surface: Surface?, width: Int, height: Int) {
            this.surface = surface
            if (this.surface != null) {
                runWithValidSurface()
            }
            surfaceWidth = width
            surfaceHeight = height
        }

        @Synchronized
        fun clearSurface() {
            if (surface == null) {
                Log.warning("[EmulationFragment] clearSurface called, but surface already null.")
            } else {
                surface = null
                surfaceWidth = null
                surfaceHeight = null
                Log.debug("[EmulationFragment] Surface destroyed.")
                when (state) {
                    State.RUNNING -> {
                        NativeLibrary.surfaceDestroyed()
                        state = State.PAUSED
                    }

                    State.PAUSED -> {
                        Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
                    }

                    else -> {
                        Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
                    }
                }
            }
        }

        private fun runWithValidSurface() {
            NativeLibrary.surfaceChanged(surface!!)
            when (state) {
                State.STOPPED -> {
                    Thread({
                        Log.debug("[EmulationFragment] Starting emulation thread.")
                        NativeLibrary.run(gamePath)
                    }, "NativeEmulation").start()
                }

                State.PAUSED -> {
                    if (AiwuSimulator.isEmulatorPausing()) {
                        return
                    }
                    Log.debug("[EmulationFragment] Resuming emulation.")
                    NativeLibrary.unPauseEmulation()
                }

                else -> {
                    Log.debug("[EmulationFragment] Bug, run called while already running.")
                }
            }
            state = State.RUNNING
        }

        private enum class State {
            STOPPED,
            RUNNING,
            PAUSED
        }
    }

    //region 修改
    private fun init25() {
        if (AiwuSimulator.ROM_PATH.isNullOrEmpty()) {
            //从模拟器进入游戏也尽量有金手指数据
            val gameId = GameIdUtil.convertGameId(game.titleId)
            val gameDir25 = File(CitraUtil.get25GameDir(), gameId)
            IOUtil.createOrExistsDir(gameDir25)
            AiwuSimulator.ROM_PATH = File(gameDir25, "虚拟的路径.aiwu").absolutePath
        }
        if (AiwuSimulator.GAME_NAME.isNullOrEmpty()) {
            AiwuSimulator.GAME_NAME = game.title
        }
        init25Params()
        AiwuSimulator.setFastMenuLayoutVisibilityChangedCallback { isShow: Boolean ->
            changeVisibilityFloatingLayout(isShow)
        }
        AiwuSimulator.addStatusChangeCallback { newStatus: Int, _: Int ->
            changeVisibilityFloatingLayout(OperateManager.isResumeStatus(newStatus))
        }
        AiwuSimulator.setKeyThemeChangeListener { keyThemeGroupBean, isInit ->
            if (!isInit) {
                //按键皮肤更改后刷新悬浮窗里的按键图标
                refreshFloatingScreenLayout()
                refreshFloatingSwitchBottomScreenLayout()
            }
        }
        AiwuSimulator.setBackgroundChangeListener { colorInt, isInit ->
            lifecycleScope.launch(Dispatchers.Default) {
                val glRgbaArray = ColorUtil.convertColorIntToGlClearColor(colorInt)
                FloatSetting.BG_RED.saveValue(glRgbaArray[0])
                FloatSetting.BG_GREEN.saveValue(glRgbaArray[1])
                FloatSetting.BG_BLUE.saveValue(glRgbaArray[2])
                //透明度，暂时不使用：原版的C层没开放，再考虑到背景带透明度效果也不好
                NativeLibrary.reloadSettings()
                //恢复模拟，使设置生效
                if (AiwuSimulator.isEmulatorPausing()) {
                    withContext(Dispatchers.Main) {
                        AiwuSimulator.pauseOrResumeEmulator(false)
                        delay(150)
                        AiwuSimulator.pauseOrResumeEmulator(true)
                    }
                }
            }
        }
        StateManager.instance.init(this)
        AiwuSimulator.setCallback(object : SimulatorCallback {
            override fun pause() {
                emulationState.pause()
            }

            override fun resume() {
                emulationState.unpause()
            }

            override fun onMenuClick(
                view: View?,
                menu: Menu,
                param: Any?,
                isFromSettingDialog: Boolean,
                autoDismissOrCollapse: Boolean,
                dismissOrCollapseCallback: SimulatorCallback.OnMenuClickCallback?
            ) {
                when (menu) {
                    Menu.RESTART_GAME -> {
                        showReloadDialog()
                    }

                    Menu.EXIT_GAME -> {
                        showExitDialog()
                    }

                    Menu.QUICK_SETTING -> {
                        if (!autoDismissOrCollapse && dismissOrCollapseCallback != null) {
                            dismissOrCollapseCallback.dismissOrCollapse()
                        }
                        context?.let {
                            FastSettingDialog(
                                emulationActivity, this@EmulationFragment,
                                screenAdjustmentUtil
                            ).show()
                        }
                    }

                    Menu.EMU_SETTING -> {
                        dismissOrCollapseCallback?.dismissOrCollapse()
                        SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
                    }

                    Menu.SOUND -> {
                        setSoundEnable(param as Boolean)
                    }

                    Menu.SCREEN_ORIENTATION -> {
                        dismissOrCollapseCallback?.dismissOrCollapse()
                        resetScreenLayout(param as Boolean)
                        PrefsHelper.instance.run {
                            removeFloatingBottomScreenLocation()
                            resetFloatingScreenLayout()
                            removeFloatingChatLocation()
                        }
                    }

                    Menu.MEMORY_CHEAT -> {
                        if (!autoDismissOrCollapse && dismissOrCollapseCallback != null) {
                            dismissOrCollapseCallback.dismissOrCollapse()
                        }
                        context?.let {
                            MemoryDialogActivity.launch(it)
                        }
                    }

                    Menu.SPEED -> {
                        setSpeedUp(param as Int)
                    }

                    Menu.SCREENSHOT -> {
                        takeScreenshot {
                            if (it == null) {
                                ToastUtil.toast("截图失败")
                                return@takeScreenshot
                            }
                            val fileName = "${game.title}_${System.currentTimeMillis()}.png"
                            IOUtil.snapAsync(it, fileName)
                        }
                    }

                    Menu.NET_PLAY -> {
                        dismissOrCollapseCallback?.dismissOrCollapse()
                        context?.let {
                            NetPlayManager.showNetPlayDialog(it)
                        }
                    }

                    else -> {}
                }
            }

            override fun setActOrientation(orientation: Int) {
                activity?.requestedOrientation = orientation
            }
        }, object : OperateListener {
            override fun onButtonClick(
                view: View?,
                player: Int,
                vararg buttons: ClickOperateButtonBean
            ) {
                InputManager.instance.onButtonClick(view, player, *buttons)
            }

            override fun onButtonRelease(
                view: View?,
                player: Int,
                vararg buttons: ClickOperateButtonBean
            ) {
                InputManager.instance.onButtonRelease(view, player, buttons)
            }

            override fun onOneKeyClick(
                view: View?,
                player: Int,
                oneKeyOperateButtonBean: OneKeyOperateButtonBean,
                buttons: MutableList<OneKeyOperate>
            ) {
                InputManager.instance.onOneKeyClick(
                    view,
                    player,
                    oneKeyOperateButtonBean.isAdvancedMode,
                    oneKeyOperateButtonBean.interval,
                    buttons
                )
            }

            override fun onTouchEvent(event: MotionEvent) {
                InputManager.instance.onTouchEvent(event)
            }
        }, InputManager.instance, InputManager.instance)
    }

    /**
     * 初始化设置参数
     */
    private fun init25Params() {
        val isEnableSound = IntSetting.AUDIO_OUTPUT_TYPE.int != 1
        AiwuSimulator.setSoundEnable(isEnableSound)
        val isFrameLimitEnabled = IntSetting.USE_FRAME_LIMIT.getBooleanValue()
        if (isFrameLimitEnabled) {
            //限制帧数
            val frameLimit = IntSetting.FRAME_LIMIT.int
            AiwuSimulator.setSpeedMultiple(ceil(frameLimit / 100f).toInt())
        } else {//不限制帧数，当做最高速
            AiwuSimulator.setSpeedMultiple(4)
        }
    }

    //启用声音
    private fun setSoundEnable(enable: Boolean) {
        lifecycleScope.launch(Dispatchers.Default) {
            IntSetting.AUDIO_OUTPUT_TYPE.saveValueAsBoolean(enable, 0, 1)
            NativeLibrary.reloadSettings()
        }
    }

    //加速
    private fun setSpeedUp(speed: Int) {
        lifecycleScope.launch(Dispatchers.Default) {
            //开启限制帧数
            IntSetting.USE_FRAME_LIMIT.saveValueAsBoolean(true)
            //设置速度
            IntSetting.FRAME_LIMIT.saveValue(speed * 100)
            NativeLibrary.reloadSettings()
        }
    }

    //重置游戏的弹窗
    private fun showReloadDialog() {
        activity?.let { activity ->
            DialogUtil.showTip(activity, "确定要重新加载当前的游戏吗？", {
                AiwuSimulator.dismissPauseDialog()
                NativeLibrary.resetGame25()
            }, null)
        }
    }

    //退出游戏的弹窗
    private fun showExitDialog() {
        activity?.let { activity ->
            DialogUtil.showTip(activity, getString(R.string.emulation_close_game_message), {
                AiwuSimulator.onExitPlay()
                AiwuSimulator.dismissPauseDialog()
                emulationState.stop()
                activity.finish()
                if (!MyApp.isLaunchFrom25Market()) {
                    //从图标启动的退出游戏后重启首页，防止内存内的数据没清空
                    val componentName = ComponentName(activity, MainActivity::class.java)
                    val mainIntent = Intent.makeRestartActivityTask(componentName)
                    startActivity(mainIntent)
                    Runtime.getRuntime().exit(0)
                }
            }, null)
        }
    }

    private fun changeVisibilityFloatingLayout(isShow: Boolean) {
        binding.floatingLayout.visibility = if (isShow) View.VISIBLE else View.GONE
    }

    fun refreshFloatingScreenLayout() {
        binding.floatingLayout.displayIvScreenLayout()
    }

    fun refreshFloatingSwitchBottomScreenLayout() {
        binding.floatingLayout.displayBottomScreenView()
    }

    /**
     * 重置屏幕布局到默认
     */
    private fun resetScreenLayout(isLandscape: Boolean) {
        val lastScreenConfigId = if (isLandscape) {
            PrefsHelper.instance.getLastLandscapeScreenConfigId()
        } else {
            PrefsHelper.instance.getLastPortraitScreenConfigId()
        }
        PrefsHelper.instance.setLastScreenConfigId(lastScreenConfigId)
        val lastScreenConfig = PrefsHelper.instance.getScreenConfig(lastScreenConfigId)
        val rotation = emulationActivity.windowManager.defaultDisplay.rotation
        if (lastScreenConfig != null && lastScreenConfig.isCustom) {
            SwitchScreenLayoutDialog.changeToCustomScreenLayout(lastScreenConfig, rotation)
        } else {
            SwitchScreenLayoutDialog.changeToSystemScreenLayout(lastScreenConfigId, rotation)
        }
    }

    //加载Amiibo
    fun loadAmiibo() {
        emulationActivity.openFileLauncher.launch(false)
    }

    override fun saveState(id: Int, filePath: String, picPath: String?) {
        lifecycleScope.launch {
            withContext(Dispatchers.IO) {
                IOUtil.createOrExistsFile(File(filePath))
            }
            AiwuSimulator.getStateCallback()?.let { callback ->
                AiwuSimulator.pauseOrResumeEmulator(false)
                callback.showLoading()
            }
            picPath?.let {
                takeScreenshot {
                    it?.let { bitmap ->
                        ImageUtils.save(bitmap, picPath, Bitmap.CompressFormat.PNG, true)
                    }
                }
            }
            NativeLibrary.saveState(id)
            //剩下的操作在NativeLibrary的onSaveStateComplete25方法
        }
    }

    /**
     * 原版只支持SAF存储路径，所以不支持云存档
     */
    override fun loadState(id: Int, path: String) {
        NativeLibrary.loadState(id)
    }

    //截图
    fun takeScreenshot(result: (Bitmap?) -> Unit) {
        if (emulationState.surfaceWidth == null || emulationState.surfaceHeight == null || emulationState.surface == null) {
            result.invoke(null)
            return
        }
        runCatching {
            val bitmap = Bitmap.createBitmap(
                emulationState.surfaceWidth!!,
                emulationState.surfaceHeight!!,
                Bitmap.Config.ARGB_8888
            )
            PixelCopy.request(emulationState.surface!!, bitmap, { copyResult ->
                if (PixelCopy.SUCCESS == copyResult) {
                    result.invoke(bitmap)
                } else {
                    result.invoke(null)
                    bitmap.recycle()
                }
            }, App.getMainHandler())
        }.onFailure {
            it.printStackTrace()
            result.invoke(null)
        }
    }

    //region 联机对战
    private val mMessageList by lazy {
        ArrayList<String>()
    }
    private val mTaskHandler by lazy {
        Handler(Looper.getMainLooper())
    }

    fun addNetPlayMessage(msg: String) {
        if (msg.isEmpty()) {
            return
        }
        mMessageList.add(msg)
        if (mMessageList.size > 10) {
            mMessageList.removeAt(0)
        }
        val sb = StringBuilder()
        for (i in mMessageList.indices) {
            sb.append(mMessageList[i])
            sb.append(System.lineSeparator())
        }
        binding.netplayMessage.run {
            text = sb.toString()
            visibility = View.VISIBLE
        }
        mTaskHandler.removeCallbacksAndMessages(null)
        mTaskHandler.postDelayed({
            binding.netplayMessage.visibility = View.INVISIBLE
            mMessageList.clear()
        }, (6 * 1000).toLong())
    }
    //endregion
    //endregion
}
